<?php

namespace convert;

/**
 * 距离类
 */
class convert
{
    private $PI = 3.14159265358979324;
    private $x_pi;

    public function __construct()
    {
        $this->x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    }

    /**
     * 判断一个坐标是否在圆内
     * 思路：判断此点的经纬度到圆心的距离  然后和半径做比较
     * 如果此点刚好在圆上 则返回true
     * @param array $point ['lng'=>'','lat'=>''] array指定点的坐标
     * @param array $circle ['center'=>['lng'=>'','lat'=>''],'radius'=>'']  中心点和半径
     */
    function is_point_in_circle($point, $circle)
    {
        $distance = $this->distance($point['lat'], $point['lng'], $circle['center']['lat'], $circle['center']['lng']);
        if ($distance <= $circle['radius']) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *  计算两个点之间的距离
     * @param float $latA 第一个点的纬度
     * @param float $lonA 第一个点的经度
     * @param float $latB 第二个点的纬度
     * @param float $lonB 第二个点的经度
     * @return float
     */
    function distance($latA, $lonA, $latB, $lonB)
    {
        $earthR = 6371000.;
        $x = cos($latA * $this->PI / 180.) * cos($latB * $this->PI / 180.) * cos(($lonA - $lonB) * $this->PI / 180);
        $y = sin($latA * $this->PI / 180.) * sin($latB * $this->PI / 180.);
        $s = $x + $y;
        if ($s > 1) $s = 1;
        if ($s < -1) $s = -1;
        $alpha = acos($s);
        $distance = $alpha * $earthR;
        return $distance;
    }

    /**
     * 判断一个坐标是否在一个多边形内（由多个坐标围成的）
     * 基本思想是利用射线法，计算射线与多边形各边的交点，如果是偶数，则点在多边形外，否则
     * 在多边形内。还会考虑一些特殊情况，如点在多边形顶点上，点在多边形边上等特殊情况。
     * @param array $point 指定点坐标
     * @param array $pts 多边形坐标 顺时针方向
     */
    function is_point_in_polygon($point, $pts)
    {
        $N = count($pts);
        $boundOrVertex = true; //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        $intersectCount = 0;//cross points count of x
        $precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        $p1 = 0;//neighbour bound vertices
        $p2 = 0;
        $p = $point; //测试点

        $p1 = $pts[0];//left vertex
        for ($i = 1; $i <= $N; ++$i) {//check all rays
            // dump($p1);
            if ($p['lng'] == $p1['lng'] && $p['lat'] == $p1['lat']) {
                return $boundOrVertex;//p is an vertex
            }

            $p2 = $pts[$i % $N];//right vertex
            if ($p['lat'] < min($p1['lat'], $p2['lat']) || $p['lat'] > max($p1['lat'], $p2['lat'])) {//ray is outside of our interests
                $p1 = $p2;
                continue;//next ray left point
            }

            if ($p['lat'] > min($p1['lat'], $p2['lat']) && $p['lat'] < max($p1['lat'], $p2['lat'])) {//ray is crossing over by the algorithm (common part of)
                if ($p['lng'] <= max($p1['lng'], $p2['lng'])) {//x is before of ray
                    if ($p1['lat'] == $p2['lat'] && $p['lng'] >= min($p1['lng'], $p2['lng'])) {//overlies on a horizontal ray
                        return $boundOrVertex;
                    }

                    if ($p1['lng'] == $p2['lng']) {//ray is vertical
                        if ($p1['lng'] == $p['lng']) {//overlies on a vertical ray
                            return $boundOrVertex;
                        } else {//before ray
                            ++$intersectCount;
                        }
                    } else {//cross point on the left side
                        $xinters = ($p['lat'] - $p1['lat']) * ($p2['lng'] - $p1['lng']) / ($p2['lat'] - $p1['lat']) + $p1['lng'];//cross point of lng
                        if (abs($p['lng'] - $xinters) < $precision) {//overlies on a ray
                            return $boundOrVertex;
                        }

                        if ($p['lng'] < $xinters) {//before ray
                            ++$intersectCount;
                        }
                    }
                }
            } else {//special case when ray is crossing through the vertex
                if ($p['lat'] == $p2['lat'] && $p['lng'] <= $p2['lng']) {//p crossing over p2
                    $p3 = $pts[($i + 1) % $N]; //next vertex
                    if ($p['lat'] >= min($p1['lat'], $p3['lat']) && $p['lat'] <= max($p1['lat'], $p3['lat'])) { //p.lat lies between p1.lat & p3.lat
                        ++$intersectCount;
                    } else {
                        $intersectCount += 2;
                    }
                }
            }
            $p1 = $p2;//next ray left point
        }

        if ($intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    /**
     * 判断一个坐标是否在一个多边形内
     * @param float $aLon 经度或者纬度
     * @param float $aLat 纬度或者经度
     * @param array $pointList 区域地点多边形点的顺序需根据顺时针或逆时针，不能乱
     * @return bool|int
     */
    function is_pt_in_polygon($aLon, $aLat, $pointList = array())
    {
        $iSum = 0;
        $iCount = count($pointList);
        if ($iCount < 3) {
            return false;
        }
        foreach ($pointList as $key => $row) {
            $pLon1 = $row[0];
            $pLat1 = $row[1];
            if ($key === $iCount - 1) {
                $pLon2 = $pointList[0][0];
                $pLat2 = $pointList[0][1];
            } else {
                $pLon2 = $pointList[$key + 1][0];
                $pLat2 = $pointList[$key + 1][1];
            }
            if ((($aLat >= $pLat1) && ($aLat < $pLat2)) || (($aLat >= $pLat2) && ($aLat < $pLat1))) {
                if (abs($pLat1 - $pLat2) > 0) {
                    $pLon = $pLon1 - (($pLon1 - $pLon2) * ($pLat1 - $aLat)) / ($pLat1 - $pLat2);
                    if ($pLon < $aLon) {
                        $iSum += 1;
                    }
                }
            }
        }
        if ($iSum % 2 != 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 计算两个经纬度之间的距离
     * @encoding UTF-8
     * @param float $lng1 经度
     * @param float $lat1 纬度
     * @param float $lng2 经度
     * @param float $lat2 纬度
     * @return number
     */
    function get_distance($lng1, $lat1, $lng2 = '', $lat2 = '', $type = 'm', $dot = 2)
    {

        if (is_array($lng1) && is_array($lat1)) {
            if (!empty($lng2)) {
                if (is_numeric($lng2)) {
                    $dot = $lng2;
                } else {
                    $type = $lng2;
                }
            }
            if (!empty($lat2) && is_numeric($lat2)) {
                $dot = $lat2;
            }
            $lng2 = $lat1[0];
            $lat2 = $lat1[1];
            $lat1 = $lng1[1];
            $lng1 = $lng1[0];
        }
        if (strpos($lng1, ',') > -1 && strpos($lat1, ',') > -1) {
            if (!empty($lng2)) {
                if (is_numeric($lng2)) {
                    $dot = $lng2;
                } else {
                    $type = $lng2;
                }
            }
            if (!empty($lat2) && is_numeric($lat2)) {
                $dot = $lat2;
            }
            list($lng2, $lat2) = explode(',', $lat1);
            list($lng1, $lat1) = explode(',', $lng1);
        }
        $d = (2 * ATAN2(SQRT(SIN(($lat1 - $lat2) * PI() / 180 / 2) * SIN(($lat1 - $lat2) * PI() / 180 / 2) + COS($lat2 * PI() / 180) * COS($lat1 * PI() / 180) * SIN(($lng1 - $lng2) * PI() / 180 / 2) * SIN(($lng1 - $lng2) * PI() / 180 / 2)), SQRT(1 - SIN(($lat1 - $lat2) * PI() / 180 / 2) * SIN(($lat1 - $lat2) * PI() / 180 / 2) + COS($lat2 * PI() / 180) * COS($lat1 * PI() / 180) * SIN(($lng1 - $lng2) * PI() / 180 / 2) * SIN(($lng1 - $lng2) * PI() / 180 / 2)))) * 6378137;
        if ($type == 'km') {
            $d = $d / 1000;
        }
        return round($d, $dot);

    }

    /**
     * 计算两个坐标之间的距离(米)
     *
     * @param float $fP1Lat 起点(纬度)
     * @param float $fP1Lon 起点(经度)
     * @param float $fP2Lat 终点(纬度)
     * @param float $fP2Lon 终点(经度)
     * @return integer
     */
    function distanceBetween($fP1Lat, $fP1Lon, $fP2Lat, $fP2Lon)
    {
        $fEARTH_RADIUS = 6378137;
        // 角度换算成弧度
        $fRadLon1 = deg2rad($fP1Lon);
        $fRadLon2 = deg2rad($fP2Lon);
        $fRadLat1 = deg2rad($fP1Lat);
        $fRadLat2 = deg2rad($fP2Lat);
        // 计算经纬度的差值
        $fD1 = abs($fRadLat1 - $fRadLat2);
        $fD2 = abs($fRadLon1 - $fRadLon2);
        // 距离计算
        $fP = pow(sin($fD1 / 2), 2) + cos($fRadLat1) * cos($fRadLat2) * pow(sin($fD2 / 2), 2);
        return intval($fEARTH_RADIUS * 2 * asin(sqrt($fP)) + 0.5);
    }

    /**
     * 计算两点地理坐标之间的距离
     * @param float $longitude1 起点经度
     * @param float $latitude1 起点纬度
     * @param float $longitude2 终点经度
     * @param float $latitude2 终点纬度
     * @param integer $unit 单位 1:米 2:公里
     * @param integer $decimal 精度 保留小数位数
     * @return mixed
     */
    function getDistance($longitude1, $latitude1, $longitude2, $latitude2, $unit = 2, $decimal = 2)
    {

        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI = 3.1415926;

        $radLat1 = $latitude1 * $PI / 180.0;
        $radLat2 = $latitude2 * $PI / 180.0;

        $radLng1 = $longitude1 * $PI / 180.0;
        $radLng2 = $longitude2 * $PI / 180.0;

        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;

        $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
        $distance = $distance * $EARTH_RADIUS * 1000;

        $m_distance = round($distance, $decimal);
        $km_distance = $m_distance / 1000;
        $km_distance = round($km_distance, $decimal);
        if ($unit == 2) {
            $distance = $distance / 1000;
        }
        return ['distance' => $m_distance, 'km_distance' => $km_distance];

    }

    /**
     * 获取以固定点为圆心特定长度半径的范围内的经纬度
     * @param float $latitude 纬度
     * @param float $longitude 经度
     * @param float $raidus 半径范围(单位：米)
     * @return mixed
     */
    function getAround($latitude, $longitude, $raidus)
    {
        $PI = 3.14159265;
        $degree = (24901 * 1609) / 360.0;
        $dpmLat = 1 / $degree;
        $radiusLat = $dpmLat * $raidus;
        $minLat = $latitude - $radiusLat;
        $maxLat = $latitude + $radiusLat;
        $mpdLng = $degree * cos($latitude * ($PI / 180));
        $dpmLng = 1 / $mpdLng;
        $radiusLng = $dpmLng * $raidus;
        $minLng = $longitude - $radiusLng;
        $maxLng = $longitude + $radiusLng;
        return array('minLng' => $minLng, 'minLat' => $minLat, 'maxLng' => $maxLng, 'maxLat' => $maxLat);
    }

    /**
     * 计算某个经纬度的周围某短距离的正方形的四个点
     * @param float $lng 经度
     * @param float $lat 维度
     * @param float $distance 这点所在圆的半径 该圆与此正方形内切默认值为0.5km
     * @return array   正方形的四个点的经纬度坐标
     */
    function returnSquarePoint($lng, $lat, $distance = 0.5)
    {
        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $dlng = 2 * asin(sin($distance / (2 * $EARTH_RADIUS)) / cos(deg2rad($lat)));
        $dlng = rad2deg($dlng);

        $dlat = $distance / $EARTH_RADIUS;
        $dlat = rad2deg($dlat);

        return array(
            'left_top' => array('lat' => $lat + $dlat, 'lng' => $lng - $dlng),
            'right_top' => array('lat' => $lat + $dlat, 'lng' => $lng + $dlng),
            'left_bottom' => array('lat' => $lat - $dlat, 'lng' => $lng - $dlng),
            'right_bottom' => array('lat' => $lat - $dlat, 'lng' => $lng + $dlng)
        );
    }
}